package org.fastsyncer.manager.scheduler;

import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;

import org.apache.commons.lang.StringUtils;
import org.fastsyncer.common.constant.CommonConstant;
import org.fastsyncer.common.constant.ConnectorConstant;
import org.fastsyncer.common.constant.MappingConstant;
import org.fastsyncer.common.entity.MappingTask;
import org.fastsyncer.common.entity.ScheduleJob;
import org.fastsyncer.common.util.ApplicationUtil;
import org.fastsyncer.common.util.UUIDUtil;
import org.fastsyncer.manager.driver.impl.AbstractDriverEvent;
import org.fastsyncer.manager.framework.DataFactory;
import org.fastsyncer.manager.scheduler.quartz.TaskQuartzJob;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.JobBuilder;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.quartz.impl.StdSchedulerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public final class SchedulerFactory extends AbstractDriverEvent {

    private static final Logger logger = LoggerFactory.getLogger(SchedulerFactory.class);

    private static volatile SchedulerFactory instance = null;

    private SchedulerFactory(){
    }
    
    public static SchedulerFactory getInstance() {
        if (null == instance) {
            synchronized (SchedulerFactory.class) {
                if (null == instance) {
                    instance = new SchedulerFactory();
                }
            }
        }
        return instance;
    }

    // 调度器工厂实例
    private static StdSchedulerFactory sf = null;
    
    private DataFactory data = DataFactory.getInstance();

    /**
     * 启动定时调度实例
     * @param defaultJobs 默认定时任务
     */
    public void start(List<ScheduleJob> defaultJobs) {
        try {
            sf = new StdSchedulerFactory();
            Properties props = new Properties();
            // 线程名称
            props.setProperty("org.quartz.scheduler.instanceName", "DefaultQuartzScheduler");
            props.setProperty("org.quartz.scheduler.rmi.export", "false");
            props.setProperty("org.quartz.scheduler.rmi.proxy", "false");
            props.setProperty("org.quartz.scheduler.wrapJobExecutionInUserTransaction", "false");
            props.setProperty("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
            // 默认分配线程个数
            String threadCount = ApplicationUtil.getKey(CommonConstant.MANAGER_QUARTZ_THREADCOUNT);
            props.setProperty("org.quartz.threadPool.threadCount", threadCount);
            // 线程优先级:最高为10,最低位1
            props.setProperty("org.quartz.threadPool.threadPriority", "5");
            props.setProperty("org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread", "true");
            props.setProperty("org.quartz.jobStore.misfireThreshold", "60000");
            props.setProperty("org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread", "true");
            props.setProperty("org.quartz.jobStore.class", "org.quartz.simpl.RAMJobStore");
            sf.initialize(props);
            sf.getScheduler().start();
            // 执行默认任务
            addTask(defaultJobs);
        } catch (SchedulerException e) {
            logger.error(e.getClass() + " > " + e.getLocalizedMessage());
        }
    }

    public void stop() {
        try {
            // 关闭调度器
            this.getScheduler().shutdown(true);
        } catch (SchedulerException e) {
            logger.error(e.getClass() + " > " + e.getLocalizedMessage());
        }
    }

    /**
     * @Title: getStdSchedulerFactory
     * @Description: 获取调度器工厂实例
     * @return: StdSchedulerFactory
     */
    public StdSchedulerFactory getStdSchedulerFactory() {
        return sf;
    }

    /**
     * @Title: Scheduler
     * @Description: 获取调度器
     * @return: Scheduler
     */
    public Scheduler getScheduler() {
        try {
            if (null != sf) {
                return sf.getScheduler();
            }
        } catch (SchedulerException e) {
            logger.error(e.getClass() + " > " + e.getLocalizedMessage());
        }
        return null;
    }

    public void addTask(List<ScheduleJob> jobs) {
        if (null == jobs || jobs.isEmpty()) {
            logger.error("Schedule jobs is null !");
            return;
        }
        try {
            Scheduler scheduler = this.getScheduler();
            for (ScheduleJob job : jobs) {
                addTask(scheduler, job);
            }
        } catch (Exception e) {
            logger.error(e.getClass() + " > " + e.getLocalizedMessage());
        }
    }

    public void addTask(ScheduleJob job) {
        if (null == job) {
            logger.error("Schedule job can not be null !");
            return;
        }
        try {
            Scheduler scheduler = this.getScheduler();
            addJob(scheduler, job);
        } catch (Exception e) {
            logger.error(e.getClass() + " > " + e.getLocalizedMessage());
        }
    }

    private void addTask(Scheduler scheduler, ScheduleJob job) throws SchedulerException {
        if (scheduler == null || null == job) {
            logger.error("Scheduler or schedule job can not be null !");
            return;
        }
        addJob(scheduler, job);
    }

    private void addJob(Scheduler scheduler, ScheduleJob job) throws SchedulerException {
        String jobId = job.getJobId();
        String jobGroup = job.getJobGroup();
        // 默认使用UUID作为唯一key
        jobId = StringUtils.isEmpty(jobId) ? UUIDUtil.getUUID() : jobId;
        jobGroup = StringUtils.isEmpty(jobGroup) ? "SystemWorkGroup" : jobGroup;

        TriggerKey triggerKey = TriggerKey.triggerKey(jobId, jobGroup);

        // 获取trigger，即在spring配置文件中定义的 bean id="myTrigger"
        CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);

        // 表达式调度构建器
        CronScheduleBuilder cron = CronScheduleBuilder.cronSchedule(job.getCronExpression());

        // 存在,删除该任务
        if (null != trigger) {
            this.deleteJob(job);
        }
        
        // 设置参数
        JobDataMap jobDataMap = new JobDataMap();
        Map<String, Object> data = job.getData();
        if (null != data) {
            for (Entry<String, Object> en : data.entrySet()) {
                jobDataMap.put(en.getKey(), en.getValue());
            }
        }

        @SuppressWarnings("unchecked")
        JobDetail jobDetail = JobBuilder.newJob(job.getJobClass()).setJobData(jobDataMap).withIdentity(jobId, jobGroup).build();

        // 按新的cronExpression表达式构建一个新的trigger
        trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(cron).build();
        scheduler.scheduleJob(jobDetail, trigger);
        logger.info("The task of schedule job ID has been queued!" + " >> " + jobId);
    }
    
    public void deleteJob(ScheduleJob job) {
        if (null == job) {
            logger.error("Schedule job can not be null !");
            return;
        }
        try {
            Scheduler scheduler = this.getScheduler();
            JobKey jobKey = new JobKey(job.getJobId(), job.getJobGroup());
            // 检查job任务是否存在
            if(scheduler.checkExists(jobKey)){
                scheduler.deleteJob(jobKey);
                logger.info("The task of schedule job ID has been removed!" + " >> " + job.getJobId());
            }
        } catch (Exception e) {
            logger.error(e.getClass() + " > " + e.getLocalizedMessage());
        }
    }
    
    public void pauseJob(ScheduleJob job) {
        if (null == job) {
            logger.error("Schedule job can not be null !");
            return;
        }
        try {
            Scheduler scheduler = this.getScheduler();
            JobKey jobKey = new JobKey(job.getJobId(), job.getJobGroup());
            if(scheduler.checkExists(jobKey)){
                scheduler.pauseJob(jobKey);
                logger.info("The task of schedule job ID has been paused!" + " >> " + job.getJobId());
            }
        } catch (Exception e) {
            logger.error(e.getClass() + " > " + e.getLocalizedMessage());
        }
    }

    /**************** 定时任务监听 **********************/
    
    /**
     * 创建定时任务
     * @param task
     * @return
     */
    public ScheduleJob createScheduleJob(MappingTask task) {
        Map<String, Map<String, String>> po = task.getPolicy();
        Map<String, String> inc = po.get(MappingConstant.POLICY_INCREMENT);
        // 获取表达式
        String quartzCron = inc.get("quartzCron");
        ScheduleJob newJob = new ScheduleJob().setJobId(task.getId()).setJobGroup("QuartzWorkGroup").setCronExpression(quartzCron).setJobClass(TaskQuartzJob.class);
        return newJob;
    }

    @Override
    public void start(String id) {
        // 获取映射配置
        MappingTask mapping = data.getMapping(id);
        
        // 如果满足定时任务条件,则创建定时任务
        if (this.validateEnabled(mapping)) {
            // 创建定时任务job
            ScheduleJob scheduleJob = this.createScheduleJob(mapping);
            if (null != scheduleJob) {
                SchedulerFactory.getInstance().addTask(scheduleJob);
                logger.info("the task of quartz job ID has been started! >>" + id);
            }
        }
    }

    @Override
    public void stop(String id) {
        // 获取映射配置
        MappingTask mapping = data.getMapping(id);
        
        // 如果满足关闭驱动条件,则删除定时任务
        if (this.validateDisabled(mapping)) {
            // 创建定时任务job
            ScheduleJob scheduleJob = this.createScheduleJob(mapping);
            if (null != scheduleJob) {
                SchedulerFactory.getInstance().deleteJob(scheduleJob);
            }
        }
    }
    
    /**
     * 是否满足启动驱动定时任务条件
     * @param task MappingTask
     * @return false/true
     */
    public boolean validateEnabled(MappingTask task) {
        try {
            String connector = task.getSourceMapping().getConnector();
            // 1.排除WebService连接器
            if (ConnectorConstant.WS_CONNECTOR.equals(connector)) {
                return false;
            }
            // 2.排除已禁用的驱动
            if (!task.isEnable()) {
                return false;
            }
            // 3.排除空策略集合
            Map<String, Map<String, String>> po = task.getPolicy();
            if (null == po) {
                return false;
            }
            // 4.排除空增量策略
            Map<String, String> inc = po.get(MappingConstant.POLICY_INCREMENT);
            if (null == inc) {
                return false;
            }
            // 5.排除非quartz模式
            if (!MappingConstant.POLICY_INCREMENT_MODEL_QUARTZ.equals(inc.get("model"))) {
                return false;
            }
            // 6.排除未开启
            if (!Boolean.valueOf(inc.get("enable"))) {
                return false;
            }
            return true;
        } catch (Exception e) {
        }
        return false;
    }

    /**
     * 是否满足关闭驱动条件
     * @param task MappingTask
     * @return false/true
     */
    public boolean validateDisabled(MappingTask task) {
        try {
            String connector = task.getSourceMapping().getConnector();
            // 1.排除WebService连接器
            if (ConnectorConstant.WS_CONNECTOR.equals(connector)) {
                return false;
            }
            // 2.排除已禁用的驱动
            if (!task.isEnable()) {
                return false;
            }
            // 3.排除空策略集合
            Map<String, Map<String, String>> po = task.getPolicy();
            if (null == po) {
                return false;
            }
            // 4.排除空增量策略
            Map<String, String> inc = po.get(MappingConstant.POLICY_INCREMENT);
            if (null == inc) {
                return false;
            }
            // 5.排除未开启
            if (Boolean.valueOf(inc.get("enable"))) {
                return false;
            }
            return true;
        } catch (Exception e) {
        }
        return false;
    }
    
}
